home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / cfengine-1.5.3 / src / locks.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-06-12  |  9.7 KB  |  418 lines

  1. /* cfengine for GNU
  2.  
  3.         Copyright (C) 1995
  4.         Free Software Foundation, Inc.
  5.  
  6.    This file is part of GNU cfengine - written and maintained 
  7.    by Mark Burgess, Dept of Computing and Engineering, Oslo College,
  8.    Dept. of Theoretical physics, University of Oslo
  9.  
  10.    This program is free software; you can redistribute it and/or modify it
  11.    under the terms of the GNU General Public License as published by the
  12.    Free Software Foundation; either version 2, or (at your option) any
  13.    later version.
  14.  
  15.    This program is distributed in the hope that it will be useful,
  16.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.    GNU General Public License for more details.
  19.  
  20.   You should have received a copy of the GNU General Public License
  21.   along with this program; if not, write to the Free Software
  22.   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
  23.  
  24. */
  25.  
  26.  
  27. /*********************************************************************/
  28. /*                                                                   */
  29. /* TOOLKIT : Locks and Signals                                       */
  30. /*                                                                   */
  31. /*********************************************************************/
  32.  
  33. /* A log file of the run times is kept for each host separately.
  34.    This records each atomic lock and the time at which it
  35.    completed, for use in computing the elapsed time. The file
  36.    format is:
  37.  
  38.    %s time:operation:operand
  39.  
  40.    Each operation (independently of operand) has a "last" inode
  41.    which keeps the time at which is last completed, for use in
  42.    calculating IfElapsed. The idea here is that the elapsed time
  43.    is from the time at which the last operation of this type
  44.    FINISHED. This is different from a lock (which is used to
  45.    allow several sub operations to coexist). Here we are
  46.    limiting activity in general to avoid "spamming".
  47.  
  48.    Each atomic operation (including operand) has a lock. The
  49.    removal of this lock causes the "last" file to be updated.
  50.    This is used to actually prevent execution of an atom
  51.    which is already being executed. If this lock has existed
  52.    for longer than the ExpireAfter time, the process owning
  53.    the lock is killed and the lock is re-established. The
  54.    lock file contains the pid. 
  55.  
  56.    This is robust to hanging locks and can be thought of as
  57.    a garbage collection mechanism for these locks.
  58.  
  59.    Last files are just inodes (empty files) so they use no disk.
  60.    The locks (which never exceed the no of running processes)
  61.    contain the pid.
  62.  
  63.    */
  64.  
  65. #include "cf.defs.h"
  66. #include "cf.extern.h"
  67.  
  68.  /* The locks need these in case of signals */
  69.  
  70. char CFLOCK[bufsize];
  71. char CFLOG[bufsize];
  72. char CFLAST[bufsize];
  73.  
  74. time_t GetLastLock();
  75. time_t CheckOldLock();
  76.  
  77. /********************************************************************/
  78.  
  79. void HandleSignal(signum)
  80.  
  81. int signum;
  82.  
  83. {
  84. sprintf(OUTPUT,"Received signal %s\n",SIGNALS[signum]);
  85. CfLog(cfinform,OUTPUT,""); 
  86.  
  87. if (signum == SIGTERM || signum == SIGINT || signum == SIGHUP)
  88.    {
  89.    ReleaseCurrentLock();
  90.    closelog();
  91.    exit(0);
  92.    }
  93. }
  94.  
  95. /************************************************************************/
  96.  
  97. GetLock(operator,operand,ifelapsed,expireafter,host,now)
  98.  
  99. char *operator, *operand, *host;
  100. int ifelapsed, expireafter;
  101. time_t now;
  102.  
  103. { struct stat statbuf;
  104.   unsigned int pid;
  105.   time_t lastcompleted = 0, elapsedtime;
  106.  
  107. if (IGNORELOCK)
  108.    {
  109.    return true;
  110.    }
  111.  
  112. if (now == 0)
  113.    {
  114.    if ((now = time((time_t *)NULL)) == -1)
  115.       {
  116.       printf("Couldn't read system clock\n");
  117.       }
  118.    return true;
  119.    }
  120.  
  121. Debug("GetLock(%s,%s,time=%d), ExpireAfter=%d, IfElapsed=%d\n",operator,operand,now,expireafter,ifelapsed);
  122.  
  123. sprintf(CFLOG,"%s/cfengine.%s.runlog",VLOGDIR,host);
  124. sprintf(CFLOCK,"%s/lock.%s.%s.%s.%s",VLOCKDIR,VCANONICALFILE,host,operator,operand);
  125. sprintf(CFLAST,"%s/last.%s.%s.%s.%s",VLOCKDIR,VCANONICALFILE,host,operator,operand);
  126.  
  127. if (strlen(CFLOCK) > 254)
  128.    {
  129.    CFLOCK[253] = '\0';  /* most nodenames are 255 chars or less */
  130.    }
  131.  
  132. if (strlen(CFLAST) > 254)
  133.    {
  134.    CFLAST[253] = '\0';  /* most nodenames are 255 chars or less */
  135.    }
  136.  
  137. /* Look for non-existent (old) processes */
  138.  
  139. lastcompleted = GetLastLock();
  140. elapsedtime = (time_t)(now-lastcompleted) / 60;
  141.  
  142. if (elapsedtime < 0)
  143.    {
  144.    sprintf(OUTPUT,"Another cfengine seems to have done %s.%s since I started\n",operator, operand);
  145.    CfLog(cfverbose,OUTPUT,"");
  146.    return false;
  147.    }
  148.  
  149. if (elapsedtime < ifelapsed)
  150.    {
  151.    sprintf(OUTPUT,"Too soon since last run with %s.%s (%u/%u minutes)\n",operator,operand,elapsedtime,ifelapsed);
  152.    CfLog(cfverbose,OUTPUT,"");
  153.    return false;
  154.    }
  155.  
  156. /* Look for existing (current) processes */
  157.  
  158. lastcompleted = CheckOldLock();
  159. elapsedtime = (time_t)(now-lastcompleted) / 60;
  160.     
  161. if (lastcompleted != 0)
  162.    {
  163.    if (elapsedtime >= expireafter)
  164.       {
  165.       sprintf(OUTPUT,"Lock %s expired...(after %u/%u minutes)\n",CFLOCK,elapsedtime,expireafter);
  166.       CfLog(cfinform,OUTPUT,"");
  167.       
  168.       pid = GetLockPid();
  169.  
  170.       if (pid == -1)
  171.      {
  172.      sprintf(OUTPUT,"Illegal pid in corrupt lock %s - ignoring lock\n",CFLOCK);
  173.      CfLog(cferror,OUTPUT,"");
  174.      }
  175.       else
  176.      {
  177.      Verbose("Trying to kill expired cfengine\n");
  178.      kill(pid,SIGCONT);
  179.      sleep(3);
  180.      kill(pid,SIGINT);
  181.      sleep(1);
  182.      kill(pid,SIGTERM);
  183.      sleep(5);
  184.      kill(pid,SIGKILL);
  185.      sleep(1);
  186.  
  187.      if (kill(pid,SIGTERM) == ESRCH)
  188.         {
  189.         sprintf(OUTPUT,"Unable to kill expired process %d, exiting this time..\n",pid);
  190.         CfLog(cferror,OUTPUT,"");
  191.         FatalError("");;
  192.         }
  193.      
  194.      LockLog(pid,"Lock expired, process killed",operator,operand);
  195.      }
  196.  
  197.       unlink(CFLOCK);
  198.       }
  199.    else
  200.       {
  201.       Verbose("Couldn't obtain lock for %s (already running!)\n",CFLOCK);
  202.       return false;
  203.       }
  204.    }
  205.  
  206. SetLock();
  207. return true;
  208. }
  209.  
  210. /************************************************************************/
  211.  
  212. ReleaseCurrentLock()
  213.  
  214. { int fd;
  215.  
  216. if (IGNORELOCK)
  217.    {
  218.    return;
  219.    }
  220.  
  221. Debug("ReleaseCurrentLock(%s)\n",CFLOCK);
  222.  
  223. if (unlink(CFLOCK) == -1)
  224.    {
  225.    sprintf(OUTPUT,"Unable to remove lock %s\n",CFLOCK);
  226.    CfLog(cflogonly,OUTPUT,"unlink");
  227.    return;
  228.    }
  229.  
  230. if ((fd = creat(CFLAST,0644)) == -1)
  231.    {
  232.    sprintf(OUTPUT,"Unable to create %s\n",CFLAST);
  233.    CfLog(cferror,OUTPUT,"creat");
  234.    FatalError("");
  235.    }
  236. else
  237.    {
  238.    close(fd);
  239.    }
  240.  
  241. LockLog(getpid(),"Lock removed normally",CFLOCK,"");
  242. }
  243.  
  244.  
  245. /************************************************************************/
  246.  
  247. CountActiveLocks(now)
  248.  
  249.  /* Count the number of active locks == number of cfengines running */
  250.  
  251. time_t now;
  252.  
  253. { DIR *dirh;
  254.   struct dirent *dirp;
  255.   int count = 0;
  256.  
  257. if ((dirh = opendir(VLOCKDIR)) == NULL)
  258.    {
  259.    sprintf(OUTPUT,"Can't open directory %s\n",VLOCKDIR);
  260.    CfLog(cferror,OUTPUT,"opendir");
  261.    return 0;
  262.    }
  263.  
  264. for (dirp = readdir(dirh); dirp != NULL; dirp = readdir(dirh))
  265.    {
  266.    if (strncmp(dirp->d_name,"lock",4) == 0)
  267.       {
  268.       count++;
  269.       }
  270.    }
  271.  
  272. closedir(dirh);
  273.  
  274. return count;
  275. }
  276.  
  277. /************************************************************************/
  278. /* Level 2                                                              */
  279. /************************************************************************/
  280.  
  281. time_t GetLastLock()
  282.  
  283. { struct stat statbuf;
  284.   int fd;
  285.  
  286. bzero(&statbuf,sizeof(statbuf));
  287.  
  288. Debug("GetLastLock()\n");
  289.  
  290. if (stat(CFLAST,&statbuf) == -1)
  291.    {
  292.    
  293.    /* Do this to prevent deadlock loops from surviving if IfElapsed > T_sched */
  294.    
  295.    if ((fd = creat(CFLAST,0644)) == -1)
  296.       {
  297.       sprintf(OUTPUT,"Unable to create %s\n",CFLAST);
  298.       CfLog(cferror,OUTPUT,"creat");
  299.       FatalError("");
  300.       }
  301.    else
  302.       {
  303.       close(fd);
  304.       }
  305.    return 0;
  306.    }
  307. else
  308.    {
  309.    return statbuf.st_mtime;
  310.    }
  311. }
  312.  
  313. /************************************************************************/
  314.  
  315. time_t CheckOldLock()
  316.  
  317. { struct stat statbuf;
  318.  
  319. bzero(&statbuf,sizeof(statbuf));
  320.  
  321. Debug("CheckOldLock()\n");
  322.  
  323. if (stat(CFLOCK,&statbuf) == -1)
  324.    {
  325.    return 0;
  326.    }
  327. else
  328.    {
  329.    return statbuf.st_mtime;
  330.    }
  331. }
  332.  
  333. /************************************************************************/
  334.  
  335. GetLockPid()
  336.  
  337. { FILE *fp;
  338.   int pid = -1;
  339.  
  340. if ((fp = fopen(CFLOCK,"r")) == NULL)
  341.    {
  342.    CfLog(cferror,"GetLock weird error, lock disappeared!\n","fopen");
  343.    FatalError("");
  344.    }
  345.  
  346. fscanf(fp,"%d",&pid);
  347.  
  348. fclose(fp);
  349.  
  350. return pid;
  351. }
  352.  
  353. /************************************************************************/
  354.  
  355. SetLock()
  356.  
  357. { FILE *fp;
  358.  
  359. Debug("SetLock(%s)\n",CFLOCK);
  360.  
  361. if ((fp = fopen(CFLOCK,"w")) == NULL)
  362.    {
  363.    sprintf(OUTPUT,"GetLock: can't open new lock file %s\n",CFLOCK);
  364.    CfLog(cferror,OUTPUT,"fopen");
  365.    FatalError("");;
  366.    }
  367.  
  368. fprintf(fp,"%d\n",getpid());
  369.  
  370. fclose(fp);
  371. chmod(CFLOCK,0644); 
  372. }
  373.  
  374. /************************************************************************/
  375.  
  376. LockLog(pid,str,operator,operand)
  377.  
  378. int pid;
  379. char *str, *operator, *operand;
  380.  
  381. { FILE *fp;
  382.   char buffer[maxvarsize];
  383.   struct stat statbuf;
  384.   time_t tim;
  385.  
  386. Debug("LockLog(%s)\n",str);
  387.  
  388. if ((fp = fopen(CFLOG,"a")) == NULL)
  389.    {
  390.    sprintf(OUTPUT,"GetLock: can't open log file %s\n",CFLOG);
  391.    CfLog(cferror,OUTPUT,"fopen");
  392.    FatalError("");
  393.    }
  394.  
  395. if ((tim = time((time_t *)NULL)) == -1)
  396.    {
  397.    Debug("Cfengine: couldn't read system clock\n");
  398.    }
  399.  
  400. sprintf(buffer,"%s",ctime(&tim));
  401.  
  402. Chop(buffer);
  403.  
  404. fprintf(fp,"%s:%s:pid=%d:%s:%s\n",buffer,str,pid,operator,operand);
  405.  
  406. fclose(fp);
  407.  
  408. if (stat(CFLOG,&statbuf) != -1)
  409.    {
  410.    if (statbuf.st_size > CFLOGSIZE)
  411.       {
  412.       Verbose("Rotating lock-runlog file\n");
  413.       RotateFiles(CFLOG,2);
  414.       }
  415.    }
  416. }
  417.  
  418.